07. Use the Fake Repository inside a ViewModel

L5 P3 A03 Use The Fake Repository Inside A ViewModel V2

In this task you'll use a fake class inside of a ViewModel. This will involve the use of a ViewModelProvider.Factory; this was explained in the Developing Android Apps with Kotlin Course's Lesson 5: App Architecture (UI Layer) - Concept 22. Exercise: Add a ViewModelFactory.

Step 1: Make and use a ViewModelFactory in TasksViewModel

You'll start with just updating the classes and test related to the Tasks screen.

  1. Open TasksViewModel.
  2. Change the constructor of TasksViewModel to take in TasksRepository rather than constructing it inside the class:

TasksViewModel.kt

// REPLACE
class TasksViewModel(application: Application) : AndroidViewModel(application) {

    private val tasksRepository = DefaultTasksRepository.getRepository(application)

    // Rest of class
}


// WITH

class TasksViewModel( private val tasksRepository: TasksRepository ) : ViewModel() { 
    // Rest of class 
}

Since you changed the constructor, you now need to use a ViewModelProvider.Factory to construct TasksViewModel. You'll put the factory class in the same file as the TasksViewModel, but you could also put it in its own file.

  1. At the bottom of the TasksViewModel file, outside the class, add a TasksViewModelFactory which takes in a plain TasksRepository:

TasksViewModel.kt

@Suppress("UNCHECKED_CAST")
class TasksViewModelFactory (
    private val tasksRepository: TasksRepository
) : ViewModelProvider.NewInstanceFactory() {
    override fun <T : ViewModel> create(modelClass: Class<T>) =
        (TasksViewModel(tasksRepository) as T)
}

Now that you have the factory, use it wherever you construct your view model.

  1. Update TasksFragment to use the factory:

TasksFragment.kt

// REPLACE
private val viewModel by viewModels<TasksViewModel>()

// WITH

private val viewModel by viewModels<TasksViewModel> {
    TasksViewModelFactory(DefaultTasksRepository.getRepository(requireActivity().application))
}
  1. Run your app code and make sure everything is still working!

Step 2: Use FakeTestRepository inside TasksViewModelTest

Now instead of using the real repository in your view model tests, you can use the fake repository.

  1. Open up TasksViewModelTest.
  2. Add a FakeTestRepository property in the TasksViewModelTest:

TasksViewModelTest.kt TKTK check

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {

    // Use a fake repository to be injected into the viewmodel
    private lateinit var tasksRepository: FakeTestRepository

    // Rest of class
}
  1. Update the setupViewModel method to make a FakeTestRepository with three tasks and then construct the tasksViewModel with this repository.

TasksViewModelTest.kt

    @Before
    fun setupViewModel() {
        // We initialise the tasks to 3, with one active and two completed
        tasksRepository = FakeTestRepository()
        val task1 = Task("Title1", "Description1")
        val task2 = Task("Title2", "Description2", true)
        val task3 = Task("Title3", "Description3", true)
        tasksRepository.addTasks(task1, task2, task3)

        tasksViewModel = TasksViewModel(tasksRepository)

    }
  1. Because you are no longer using the AndroidX Test ApplicationProvider.getApplicationContext code, you can also remove the @RunWith(AndroidJUnit4::class) annotation.
  2. Run your tests, make sure they all still work!

By using constructor dependency injection, you've now removed the DefaultTasksRepository as a dependency and replaced it with your FakeTestRepository in the tests.

Step 3. Also Update TaskDetail Fragment and ViewModel

Following the same sets you just completed, on your own, do the same steps for TaskDetailFragment and TaskDetailViewModel:

  1. Update the TaskDetailViewModel constructor to take in a TasksRepository.
  2. Add a TaskDetailViewModelFactory.
  3. Update the TaskDetailFragment to use the factory.

Remember to run your application code! The app should work exactly the same, even though you refactored the code.


If you'd like to avoid building a separate factory for each view model, check out the Architecture Blueprints reactive sample, which shows a slightly more complicated version of the tests you are building. It includes a generic ViewModelFactory that can generate any view model needed and this extension function.


You are now able to use a FakeTestRepository instead of the real repository in TasksFragment and TasksDetailFragment.